package boids.preys;

import java.util.ArrayList;

import boids.predators.Predator;

import util.Vector;

public class LinearPrey extends AbstractPrey implements Prey
{			
	private static double SEPARATION_WEIGHT = 1;
	private static double COHESION_WEIGHT = 1;
	private static double ALIGNMENT_WEIGHT = 1;
	private static double ESCAPE_WEIGHT = 1;
	
	public LinearPrey(Vector location, Vector velocity, double width, double height)
	{
		super(location, velocity, width, height);
	}
	
	public void update(ArrayList<Prey> boids, ArrayList<Predator> predators)
	{
		velocity = new Vector();
		
		ArrayList<Prey> neighbors = findNeighbors(boids);
//		ArrayList<Predator> seenPredators = findPredators(predators);
		
		if (neighbors.size() > 0)
		{
			Vector cohesion = cohesion(neighbors).multiply(COHESION_WEIGHT);
			Vector separation = separation(neighbors).multiply(SEPARATION_WEIGHT);
			Vector alignment = alignment(neighbors).multiply(ALIGNMENT_WEIGHT);
			
			Vector acceleration = cohesion.add(separation.add(alignment));
			velocity.limit(MAX_FORCE);
			velocity.add(acceleration);
		}
		
		velocity.limit(MAX_SPEED);
		location.add(velocity);
	}
	
	private Vector escape(ArrayList<Predator> predators)
	{		
		Vector escapeMean = new Vector();
		
		for (Predator p : predators)
		{
			escapeMean.add(p.getLocation());
		}
		
		Vector escapeForce = Vector.substract(location, escapeMean);
		double distance = escapeForce.magnitude();
		escapeForce.limit(MAX_FORCE);
		escapeForce.multiply(distance / PERCEPTION_RADIUS);
		return escapeForce.cutOff(MIN_FORCE);
	}
	
	private Vector alignment(ArrayList<Prey> neighbors)
	{
		Vector alignmentForce = new Vector();
		
		for (Prey b : neighbors)
		{
			alignmentForce.add(b.getVelocity());
		}
		
		alignmentForce.divide(neighbors.size());
		double magnitude = alignmentForce.magnitude();
		
		alignmentForce.limit(MAX_FORCE);
		alignmentForce.multiply(magnitude / MAX_SPEED);
		return alignmentForce.cutOff(MIN_FORCE);
	}
	
	
	private Vector separation(ArrayList<Prey> neighbors)
	{
		Vector separationMean = new Vector();
		int counter = 0;
		
		for (Prey b : neighbors)
		{
			double distance = location.euclDistance(b.getLocation());
			
			if (distance <= SEPARATION_RADIUS)
			{
				separationMean.add(b.getLocation());
				counter ++;
			}
		}
		
		if (counter == 0)
		{
			return new Vector();
		}
		
		separationMean.divide(counter);
		
		Vector separationForce = Vector.substract(location, separationMean);
		double magnitude = separationForce.magnitude();
		separationForce.limit(MAX_FORCE);
		separationForce.multiply(1 - (magnitude / SEPARATION_RADIUS));
		
		return separationForce.cutOff(MIN_FORCE);
	}
	
	private Vector cohesion(ArrayList<Prey> neighbors)
	{
		Vector cohesionMean = new Vector();
		
		for (Prey b : neighbors)
		{
			cohesionMean.add(b.getLocation());
		}
		
		cohesionMean.divide(neighbors.size());
		
		Vector cohesionForce = Vector.substract(cohesionMean, location);
		double distance = cohesionForce.magnitude();
		cohesionForce.limit(MAX_FORCE);
		cohesionForce.multiply(distance / PERCEPTION_RADIUS);

		return cohesionForce.cutOff(MIN_FORCE);
	}
}
